/*
 * Copyright 2009 Jamie Gennis
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.code.ggdi;

import com.google.gwt.rpc.client.impl.RemoteException;
import com.google.gwt.rpc.server.ClientOracle;
import com.google.gwt.rpc.server.RPC;
import com.google.gwt.rpc.server.RpcServlet;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import java.io.OutputStream;
import java.lang.reflect.Method;

/**
 * A servlet that dispatches new-style GWT deRPC calls (i.e. {@code RpcService}
 * calls) to a Guice-injected instance of the remote service interface.
 *
 * @author jgennis
 */
@Singleton
public class GgdiRpcServlet extends RpcServlet {

  @Inject
  Injector injector;

  @Override
  public void processCall(ClientOracle clientOracle, String payload, OutputStream stream)
      throws SerializationException {
    assert clientOracle != null : "clientOracle";
    assert payload != null : "payload";
    assert stream != null : "stream";

    try {
      RPCRequest rpcRequest = RPC.decodeRequest(payload, null, clientOracle);
      Method rpcMethod = rpcRequest.getMethod();
      Class<?> rpcIntf = rpcMethod.getDeclaringClass();
      Object rpcImpl = injector.getInstance(rpcIntf);
      RPC.invokeAndStreamResponse(rpcImpl, rpcMethod, rpcRequest.getParameters(), clientOracle,
          stream);
    } catch (RemoteException ex) {
      throw new SerializationException("An exception was sent from the client", ex.getCause());
    } catch (IncompatibleRemoteServiceException ex) {
      log("An IncompatibleRemoteServiceException was thrown while processing this call.", ex);
      RPC.streamResponseForFailure(clientOracle, stream, ex);
    }
  }
}
